using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Threading;
using WPFCommon.FileManipulation;
using WPFCommon.ViewModels.Base;

namespace WPFCommon.ViewModels.Message
{
    /// <summary>
    /// Log items to ui with queuing
    /// and log to file when items overflow
    /// </summary>
    public class MessageQueueViewModel : ViewModelBase
    {
        #region private filed

        private ObservableCollection<LogItemViewModel> _displayItems = new ObservableCollection<LogItemViewModel>();
        private readonly uint _maxSize;
        private readonly float _preservedPercent;
        private readonly string _savePath;
        private readonly ConcurrentQueue<LogItemViewModel> _itemQueue = new ConcurrentQueue<LogItemViewModel>();

        #endregion

        #region ctor

        public MessageQueueViewModel(string savePath, uint maxSize = 100, float preservedPercent = 0.7f,
            double dequeueFrequency = 500)
        {
            _maxSize = maxSize;
            _preservedPercent = preservedPercent;
            _savePath = savePath;

            var timer = new DispatcherTimer()
            {
                Interval = TimeSpan.FromMilliseconds(dequeueFrequency), IsEnabled = true
            };

            timer.Tick += DisplayAvailableItems;
        }

        private void DisplayAvailableItems(object sender, EventArgs e)
        {
            if (!_itemQueue.TryDequeue(out var item)) return;

            System.Windows.Application.Current.Dispatcher?.InvokeAsync(() =>
            {
                DisplayItems.Insert(0, item);

                if (DisplayItems.Count <= _maxSize) return;
                
                var preserveSize = (int) (_maxSize * _preservedPercent);
                var saveItems = DisplayItems.Skip(preserveSize);
                DisplayItems = new ObservableCollection<LogItemViewModel>(DisplayItems.Take(preserveSize));
                
                // Save items that are removed form ui
                Task.Run(() =>
                {
                    IList<string> saveLines = ToSaveLines(saveItems);
                    FileHelper.AppendLinesToFile(saveLines, _savePath);
                });
            });
        }

        private IList<string> ToSaveLines(IEnumerable<LogItemViewModel> saveItems)
        {
            return saveItems.Select(item =>
            {
                var messageToSave = item.SaveMessage == null ? item.SaveMessage : item.DisplayMessage;
                return $"{item.LogTime.ToString(SaveTimeFormat)}:{messageToSave}";
            }).ToList();
        }

        #endregion

        #region props

        public ObservableCollection<LogItemViewModel> DisplayItems
        {
            get => _displayItems;
            set
            {
                _displayItems = value;
                OnPropertyChanged();
            }
        }

        public string SaveTimeFormat { get; set; } = "MMdd-HHmm-ss";

        #endregion


        #region api

        public void EnqueueItem(LogItemViewModel item)
        {
            _itemQueue.Enqueue(item);
        }

        #endregion
    }
}